﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using WoWConnecter.Chat;

namespace WoWConnecter.Entities
{
    /// <summary>
    /// Represents our character in the game
    /// </summary>
    public partial class UserCharacter : Character
    {
        private const int CharEnumItemBytes = 4 + 1 + 4;
        private const int CharEnumItemCount = 23; // 19 items, 4 bags

        public RealmClient Client { get; internal set; }

        private Queue<RealmPacketIn> mPendingPackets;
        private ReaderWriterLockSlim mLock;
        private PacketHandler mPacketHandler;
        private Thread mThread;
        private Dictionary<string, Channel> mChannels;
        private Dictionary<EntityId, Character> mKnownCharacters;
        private Dictionary<EntityId, List<Action<Character>>> mPendingCharacterRequests;

        public event Action<int, Channel> JoinChannelResult;

        public UserCharacter(RealmPacketIn packet)
        {
            Id = packet.ReadEntityId();
            Name = packet.ReadCString();

            Race = (RaceId)packet.ReadByte();
            Class = (ClassId)packet.ReadByte();

            Gender = (GenderType)packet.ReadByte();
            Skin = packet.ReadByte();
            Face = packet.ReadByte();
            HairStyle = packet.ReadByte();
            HairColour = packet.ReadByte();
            FacialHair = packet.ReadByte();

            Level = packet.ReadByte();

            Zone = packet.ReadInt32();
            Region = packet.ReadInt32();

            X = packet.ReadFloat();
            Y = packet.ReadFloat();
            Z = packet.ReadFloat();

            GuildId = packet.ReadUInt32();
            Flags = (CharEnumFlags)packet.ReadInt32();

            CustomizationFlags = packet.ReadInt32();

            byte firstLogin = packet.ReadByte(); // 3.2.0 Unknown

            Pet pet = new Pet(packet);

            byte[] items = packet.ReadBytes(20 * CharEnumItemBytes);
        }

        public UserCharacter(string name, RaceId race, ClassId chrClass, byte skin, byte face, byte hairStyle, byte hairColour)
            : base(name, race, chrClass, skin, face, hairStyle, hairColour)
        {

        }

        public void StartProcessing(PacketHandler packetHandler)
        {
            Thinking = true;
            mPendingPackets = new Queue<RealmPacketIn>();
            mChannels = new Dictionary<string, Channel>();
            mKnownCharacters = new Dictionary<EntityId, Character>();
            mPendingCharacterRequests = new Dictionary<EntityId, List<Action<Character>>>();
            mLock = new ReaderWriterLockSlim();
            mPacketHandler = packetHandler;

            mThread = new Thread(new ThreadStart(Start));
            mThread.IsBackground = true;
            mThread.Name = "Character Packet Processor";
            mThread.Start();
        }

        private void Start()
        {
            while (Thinking)
            {
                List<RealmPacketIn> packets = new List<RealmPacketIn>();

                //Read all packets out of the incoming queue
                mLock.EnterWriteLock();
                if (mPendingPackets.Count > 0)
                    while (mPendingPackets.Count != 0)
                    {
                        packets.Add(mPendingPackets.Dequeue());
                    }
                mLock.ExitWriteLock();

                foreach (RealmPacketIn packet in packets)
                    mPacketHandler.Handle(packet);

                Think();

                Thread.Sleep(45);
            }
        }

        public void Join(string channel, string password = "")
        {
            //this should determine what kind of channel we are joining but for now, custom channels only
            SendChannelJoin(channel, 0x8, password);
        }

        protected virtual void Think()
        {

        }

        /// <summary>
        /// Queue a packet for processing into the client
        /// </summary>
        /// <param name="packet">Incoming packet</param>
        public void QueuePacket(RealmPacketIn packet)
        {
            mLock.EnterWriteLock();
            mPendingPackets.Enqueue(packet);
            mLock.ExitWriteLock();
        }

        public Channel GetChannel(string name)
        {
            Channel channel = null;

            mChannels.TryGetValue(name, out channel);

            return channel;
        }

        /// <summary>
        /// Send a packe to the server
        /// </summary>
        /// <param name="packet"></param>
        public void Send(RealmPacketOut packet)
        {
            Client.Send(packet);
        }

        public void WhisperTo(string chrName, string message, params object[] args)
        {

        }

        public Character GetCharacter(EntityId id, Action<Character> callback = null)
        {
            Character character = null;

            if (!mKnownCharacters.TryGetValue(id, out character))
            {
                if (callback != null)
                {
                    if (mPendingCharacterRequests.ContainsKey(id))
                        mPendingCharacterRequests[id].Add(callback);
                    else
                    {
                        List<Action<Character>> list = new List<Action<Character>>(1);
                        mPendingCharacterRequests.Add(id, list);
                        list.Add(callback);
                        SendCharacterInfoRequest(id);
                    }
                }
            }

            return character;
        }

        /// <summary>
        /// At this time we dont want to whisper ourselves
        /// </summary>
        /// <param name="message"></param>
        /// <param name="args"></param>
        public new void Whisper(string message, params object[] args)
        {

        }

        public void Say(string message, params object[] args)
        {

        }

        public void Yell(string message, params object[] args)
        {

        }
    }
}
